#include <common.h>
#include <asm/io.h>

extern uint32_t					 mCmd_Write;
extern uint32_t					 mCmd_Eares;
extern uint32_t					 mCmd_Pp;
#define SECTOR_SIZE (1024 * 64)



#define SPI_CFG_CMD_READ_ID              (0x90<<24)
#define SPI_CFG_CMD_RDID                 (0x9F<<24)
#define SPI_CFG_CMD_RDSR                 (0x05<<24)
#define SPI_CFG_CMD_SE_64KB              (0xD8<<24)
#define SPI_CFG_CMD_WREN                 (0x06<<24)
#define SPI_CFG_CMD_READ                 (0x03<<24)
#define SPI_CFG_FLASH_SEL_0              (0x0<<19)
#define SPI_CFG_ADDR_TRANSFER            (0x1<<15)  /* cmd_addr, 1: , 0:    */

#define SPI_CFG_DUMMY_SEND               (0x1<<14)  /* 1:send, 表示读数据有延迟        , 0:not send */
#define SPI_CFG_DUMMY_CYCLE              (0x1<<7)   /* 当 latency=1 表示读数据有延迟, 延迟的周期数 */

#define SPI_CFG_DATA_TRANSFER			 (0x1<<13)	/* 1:send, data_transfer , 0:not data_transfer */
#define SPI_CFG_ADDR_CNT_3               (0x0<<12)  /* address_cnt = 3 */
#define SPI_CFG_ADDR_CNT_4               (0x1<<12)  /* address_cnt = 4 */

#define SPI_CFG_RW_NUM_1                 (0x0<<3)
#define SPI_CFG_RW_NUM_2                 (0x1<<3)
#define SPI_CFG_RW_NUM_3                 (0x2<<3)
#define SPI_CFG_RW_NUM_4                 (0x3<<3)
#define SPI_CFG_RW_NUM_8                 (0x7<<3)

#define SPI_ACCESS_CMD_MODE_MASK         (0x3<<6)
#define SPI_ACCESS_ADDR_MODE_MASK        (0x1<<5)

#define spi_cmd_read_id()      SPI_CFG_FLASH_SEL_0  \
							  | SPI_CFG_CMD_READ_ID   \
                              | SPI_CFG_ADDR_TRANSFER     \
                              | SPI_CFG_ADDR_CNT_3    \
                              | SPI_CFG_DATA_TRANSFER		\
                              | SPI_CFG_RW_NUM_4;   
//0x9F002040
#define spi_cmd_rdid()                SPI_CFG_FLASH_SEL_0	\
									| SPI_CFG_CMD_RDID		\
									| SPI_CFG_DATA_TRANSFER		\
									| SPI_CFG_RW_NUM_4; 	

#define spi_write_addr(addr)                           \
      *(u32 *)SPI_ADDR_PORT_ADDR = addr;

#define spi_read_h_data(value)                         \
      value = *(u32 *)SPI_HDATA_PORT_ADDR;

#define spi_read_l_data(value)                         \
      value = *(u32 *)SPI_LDATA_PORT_ADDR;

typedef struct spi_reg  
{
	uint32_t reg_flash_cap;
	uint32_t reg_rd_cfg;
	uint32_t reg_wr_cfg;
	uint32_t reg_flush_reg;
	uint32_t reg_cmd_port;
	uint32_t reg_addr_port;
	uint32_t reg_hd_port;
	uint32_t reg_ld_port;
	uint32_t reg_fun_set;
	uint32_t reg_err_log;
	uint32_t reg_wp_reg;
	uint32_t reg_mode_reg;
}spi_reg_t;


spi_reg_t * spi_reg_info = (void *)0x28014000;

int  spi_erase_sector(uint32_t BlockAddress)
{
	uint32_t cmd_id;
	int ret = 0;
	cmd_id = mCmd_Pp;
    //printf("cmd_pp is 0x%x\n",mCmd_Pp);
	writel(0x400000 | (cmd_id << 24),&spi_reg_info->reg_cmd_port);
	writel(0x1,&spi_reg_info->reg_ld_port);
	// MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
	//  MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");
	cmd_id = mCmd_Eares;

	writel(0x408000 | (cmd_id << 24),&spi_reg_info->reg_cmd_port);
	writel(BlockAddress,&spi_reg_info->reg_addr_port);
	writel(0x1,&spi_reg_info->reg_ld_port);
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");
	return ret;
}

int qspinor_erase(uint64_t addr, int len)
{	
	//assert((len % SECTOR_SIZE == 0 ));

	while (len)
	{
		//write_enable();
		spi_erase_sector(addr);
		addr += SECTOR_SIZE;
		len -= SECTOR_SIZE;
	}
}



int  spi_write_word(uint32_t Address, uint32_t Value)
{
	uint32_t cmd_id = 0;
	uint32_t ret = 0;
	if (Value == 0xffffffff)
	return -1;

	if(Address % 4 != 0) {
		printf( "Address not aligne 4 byte \n");
		return -1;
	}

	cmd_id = mCmd_Pp;

	writel(0x400000 | (cmd_id << 24),&spi_reg_info->reg_cmd_port);
	writel(0x1,&spi_reg_info->reg_ld_port);
	//MmioWrite32(mNorFlashControlBase + REG_CMD_PORT, 0x400000 | (cmd_id << 24));
	//MmioWrite32(mNorFlashControlBase + REG_LD_PORT, 0x1);
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");
	cmd_id = mCmd_Write;
	writel(0x000208 | (cmd_id << 24),&spi_reg_info->reg_wr_cfg);
	writel(Value,(void*)Address);
	//MmioWrite32(mNorFlashControlBase + REG_WR_CFG, 0x000208 | (cmd_id << 24));
	// MmioWrite32(Address, Value);
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");
	writel(0x1,&spi_reg_info->reg_flush_reg);
	//MmioWrite32(mNorFlashControlBase + REG_FLUSH_REG, 0x1);
	return ret;
}

#if 1
/*
 * function:
 * input   :
 * output  :
 * used    :
 * remark  :
 * version :
 * notice  :  pp_num must less than 64, waveform of pp is 256 byte;
 *            correctness is not sure
 *            flush_buffer will be flushed, so data to be programed will be lost
 */
void spi_page_program_flush(uint32_t *src_addr, uint32_t *page_addr, uint32_t pp_num)
{
	uint32_t i = 0;
	uint32_t test[64]= {0};
	uint32_t cmd_id;
	
	for(i = 0; i < 64; i++)
		test[i] = *(src_addr+i);
		//test[i] = *(u32 *)((u64)src_addr+(i<<2));
		

	//writel(((0x0<<6)|(0x0<<5)|(0x1<<3)|0x2),&spi_reg_info->reg_wr_cfg);

	cmd_id = mCmd_Pp;

	writel(0x400000 | (cmd_id << 24),&spi_reg_info->reg_cmd_port);
	writel(0x1,&spi_reg_info->reg_ld_port);
	
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");
	cmd_id = mCmd_Write;
	writel(0x000208 | (cmd_id << 24),&spi_reg_info->reg_wr_cfg);//spi_cmd_wren(0);
//	writel(Value,(void*)Address);
	
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");
	

	


	for(i = 0; i < 64; i++)
		*(page_addr + i) = test[i];
		//*(u32 *)((u64)page_addr + (i<<2)) = test[i];

	writel(0x1,&spi_reg_info->reg_flush_reg);//spi_flush;
}


/*
 * function:
 * input   :
 * output  :
 * used    :  must write in 32bits
 * remark  :
 * version :
 */
int spi_page_program(uint32_t *src_addr, uint32_t *page_addr, uint32_t pp_num)
{
	uint32_t i = 0, add = 0;

	if(pp_num <= 256){
		add = 1;
	}else {
		if((pp_num%256)>0)
			add = pp_num/256+1;
		else
			add = pp_num/256;
	}
	//e_printf("pp_num = 0x%x\n", pp_num);
	//e_printf("add = 0x%x\n", add);

	for(i = 0; i < add; i++)
		spi_page_program_flush(src_addr + i*64, page_addr + i*64, i);

	return 0;

}
#endif

/* Read Manufacturer and Device ID (READ_ID: 90h) */
u16 spi_read_id(void)
{
    u16 id_data=0;
    u8 data = 0;

	writel(spi_cmd_read_id(), &spi_reg_info->reg_cmd_port);
	writel(0x1, &spi_reg_info->reg_ld_port);
	
	//writel(0x0, &spi_reg_info->reg_addr_port);
	//writel(0x0, &spi_reg_info->reg_addr_port);
	writel(0x100000, &spi_reg_info->reg_addr_port);
	//writel(0x1, &spi_reg_info->reg_ld_port);


	
	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");

    data = readl( &spi_reg_info->reg_hd_port);
	printf("reg_hd_port = %x\n", data);
    id_data = (u16)(data<<16);
    data = readl( &spi_reg_info->reg_ld_port);
	printf("reg_ld_port = %x\n", data);
    id_data |= data;

	
    return id_data;
}

/* Read Identification (RDID: 9Fh) */
u16 spi_rdid(void)
{
    u16 id_data=0;

	writel(spi_cmd_rdid(), &spi_reg_info->reg_cmd_port);
	//writel(0x1, &spi_reg_info->reg_ld_port);
	
	//writel(0x0, &spi_reg_info->reg_addr_port);
	//writel(0x0, &spi_reg_info->reg_addr_port);
	//writel(0x1, &spi_reg_info->reg_addr_port);
	//writel(0x1, &spi_reg_info->reg_ld_port);

	asm volatile ("isb sy":::"cc");
	asm volatile ("dsb sy":::"cc");

	printf("(RDID: 9Fh) reg2_ld_port = %x\n", readl(&spi_reg_info->reg_ld_port));
	printf("(RDID: 9Fh) reg2_hd_port = %x\n", readl(&spi_reg_info->reg_hd_port));

	
    id_data = readl( &spi_reg_info->reg_ld_port);

    return id_data;
}





